Micron Document
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
| SparkN0de-git | SparkN0de |
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

Node / ReticulumProjects / Sideband.git / files / sbapp / pyogg / opus_buffered_encoder.py

Displaying Raw • Download


sbapp/pyogg/opus_buffered_encoder.py f8b3e3760b831745cf10d97aeae64e9c00a136a9 (f8b3e376) Text, 15.51 KB

Tff7b72import T7ee787copy
Tff7b72import T7ee787ctypes
Tff7b72from T7ee787typing Tff7b72import Te6edf3OptionalTb4b4b4, Te6edf3ByteStringTb4b4b4, Te6edf3ListTb4b4b4, Te6edf3TupleTb4b4b4, Te6edf3Callable
Tff7b72import T7ee787warnings

Tff7b72from T7ee787. Tff7b72import Te6edf3opus
Tff7b72from T7ee787.T7ee787opus_encoder Tff7b72import Te6edf3OpusEncoder
Tff7b72from T7ee787.T7ee787pyogg_error Tff7b72import Te6edf3PyOggError

Tff7b72class T56d364OpusBufferedEncoderTb4b4b4(Te6edf3OpusEncoderTb4b4b4)Tb4b4b4:
T8b949e# TODO: This could be made more efficient. We don't need a
T8b949e# deque. Instead, we need only sufficient PCM storage for one
T8b949e# whole packet. We know the size of the packet thanks to
T8b949e# set_frame_size().

Tff7b72def Tff7b72__init__Tb4b4b4(Tff7b72selfTb4b4b4) Tff7b72-Tff7b72> Tff7b72NoneTb4b4b4:
Tffa657superTb4b4b4(Tb4b4b4)Tff7b72.Td2a8ff__init__Tb4b4b4(Tb4b4b4)

Tff7b72selfTff7b72.Td2a8ff_frame_size_msTb4b4b4: Te6edf3OptionalTb4b4b4[Tffa657floatTb4b4b4] Tff7b72= Tff7b72None
Tff7b72selfTff7b72.Td2a8ff_frame_size_bytesTb4b4b4: Te6edf3OptionalTb4b4b4[Tffa657intTb4b4b4] Tff7b72= Tff7b72None

T8b949e# Buffer contains the bytes required for the next
T8b949e# frame.
Tff7b72selfTff7b72.Td2a8ff_bufferTb4b4b4: Te6edf3OptionalTb4b4b4[Te6edf3ctypesTff7b72.Td2a8ffArrayTb4b4b4] Tff7b72= Tff7b72None

T8b949e# Location of the next free byte in the buffer
Tff7b72selfTff7b72.Td2a8ff_buffer_index Tff7b72= T79c0ff0


Tff7b72def Td2a8ffset_frame_sizeTb4b4b4(Tff7b72selfTb4b4b4, Te6edf3frame_sizeTb4b4b4: Tffa657floatTb4b4b4) Tff7b72-Tff7b72> Tff7b72NoneTb4b4b4:
T8b949e""" Set the desired frame duration (in milliseconds).

Valid options are 2.5, 5, 10, 20, 40, or 60ms.

"""

T8b949e# Ensure the frame size is valid. Compare frame size in
T8b949e# units of 0.1ms to avoid floating point comparison
Tff7b72if Tffa657intTb4b4b4(Te6edf3frame_sizeTff7b72*T79c0ff10Tb4b4b4) Tff7b72not Tff7b72in Tb4b4b4[T79c0ff25Tb4b4b4, T79c0ff50Tb4b4b4, T79c0ff100Tb4b4b4, T79c0ff200Tb4b4b4, T79c0ff400Tb4b4b4, T79c0ff600Tb4b4b4]Tb4b4b4:
Tff7b72raise Te6edf3PyOggErrorTb4b4b4(
Ta5d6ff"Ta5d6ffFrame size (Tffd700{:f}Ta5d6ff) not one of Ta5d6ff"Tff7b72.Td2a8ffformatTb4b4b4(Te6edf3frame_sizeTb4b4b4)Tff7b72+
Ta5d6ff"Ta5d6ffthe acceptable valuesTa5d6ff"
Tb4b4b4)

Tff7b72selfTff7b72.Td2a8ff_frame_size_ms Tff7b72= Te6edf3frame_size

Tff7b72selfTff7b72.Td2a8ff_calc_frame_sizeTb4b4b4(Tb4b4b4)


Tff7b72def Td2a8ffset_sampling_frequencyTb4b4b4(Tff7b72selfTb4b4b4, Te6edf3samples_per_secondTb4b4b4: Tffa657intTb4b4b4) Tff7b72-Tff7b72> Tff7b72NoneTb4b4b4:
Tffa657superTb4b4b4(Tb4b4b4)Tff7b72.Td2a8ffset_sampling_frequencyTb4b4b4(Te6edf3samples_per_secondTb4b4b4)
Tff7b72selfTff7b72.Td2a8ff_calc_frame_sizeTb4b4b4(Tb4b4b4)


Tff7b72def Td2a8ffbuffered_encodeTb4b4b4(Tff7b72selfTb4b4b4,
Te6edf3pcm_bytesTb4b4b4: Tffa657memoryviewTb4b4b4,
Te6edf3flushTb4b4b4: Tffa657bool Tff7b72= Tff7b72FalseTb4b4b4,
Te6edf3callbackTb4b4b4: Te6edf3CallableTb4b4b4[Tb4b4b4[Tffa657memoryviewTb4b4b4,Tffa657intTb4b4b4,Tffa657boolTb4b4b4]Tb4b4b4,Tff7b72NoneTb4b4b4] Tff7b72= Tff7b72None
Tb4b4b4) Tff7b72-Tff7b72> Te6edf3ListTb4b4b4[Te6edf3TupleTb4b4b4[Tffa657memoryviewTb4b4b4, Tffa657intTb4b4b4, Tffa657boolTb4b4b4]Tb4b4b4]Tb4b4b4:
T8b949e"""Gets encoded packets and their number of samples.

This method returns a list, where each item in the list is
a tuple. The first item in the tuple is an Opus-encoded
frame stored as a bytes-object. The second item in the
tuple is the number of samples encoded (excluding
silence).

If `callback` is supplied then this method will instead
return an empty list but call the callback for every
Opus-encoded frame that would have been returned as a
list. This option has the desireable property of
eliminating the copying of the encoded packets, which is
required in order to form a list. The callback should
take two arguments, the encoded frame (a Python bytes
object) and the number of samples encoded per channel (an
int). The user must either process or copy the data as
the data may be overwritten once the callback terminates.

"""
T8b949e# If there's no work to do return immediately
Tff7b72if Tffa657lenTb4b4b4(Te6edf3pcm_bytesTb4b4b4) Tff7b72== T79c0ff0 Tff7b72and Te6edf3flush Tff7b72== Tff7b72FalseTb4b4b4:
Tff7b72return Tb4b4b4[Tb4b4b4] T8b949e# no work to do

T8b949e# Sanity checks
Tff7b72if Tff7b72selfTff7b72.Td2a8ff_frame_size_ms Tff7b72is Tff7b72NoneTb4b4b4:
Tff7b72raise Te6edf3PyOggErrorTb4b4b4(Ta5d6ff"Ta5d6ffFrame size must be set before encodingTa5d6ff"Tb4b4b4)
Tff7b72assert Tff7b72selfTff7b72.Td2a8ff_frame_size_bytes Tff7b72is Tff7b72not Tff7b72None
Tff7b72assert Tff7b72selfTff7b72.Td2a8ff_channels Tff7b72is Tff7b72not Tff7b72None
Tff7b72assert Tff7b72selfTff7b72.Td2a8ff_buffer Tff7b72is Tff7b72not Tff7b72None
Tff7b72assert Tff7b72selfTff7b72.Td2a8ff_buffer_index Tff7b72is Tff7b72not Tff7b72None

T8b949e# Local variable initialisation
Te6edf3results Tff7b72= Tb4b4b4[Tb4b4b4]
Te6edf3pcm_index Tff7b72= T79c0ff0
Te6edf3pcm_len Tff7b72= Tffa657lenTb4b4b4(Te6edf3pcm_bytesTb4b4b4)

T8b949e# 'Cast' memoryview of PCM to ctypes Array
Te6edf3Buffer Tff7b72= Te6edf3ctypesTff7b72.Td2a8ffc_ubyte Tff7b72* Tffa657lenTb4b4b4(Te6edf3pcm_bytesTb4b4b4)
Tff7b72tryTb4b4b4:
Te6edf3pcm_ctypes Tff7b72= Te6edf3BufferTff7b72.Td2a8fffrom_bufferTb4b4b4(Te6edf3pcm_bytesTb4b4b4)
Tff7b72except Tf85149TypeErrorTb4b4b4:
Te6edf3warningsTff7b72.Td2a8ffwarnTb4b4b4(
Ta5d6ff"Ta5d6ffBecause PCM was read-only, an extra memory Ta5d6ff"Tff7b72+
Ta5d6ff"Ta5d6ffcopy was required; consider storing PCM in Ta5d6ff"Tff7b72+
Ta5d6ff"Ta5d6ffwritable memory (for example, bytearray Ta5d6ff"Tff7b72+
Ta5d6ff"Ta5d6ffrather than bytes).Ta5d6ff"
Tb4b4b4)
Te6edf3pcm_ctypes Tff7b72= Te6edf3BufferTff7b72.Td2a8fffrom_bufferTb4b4b4(Te6edf3pcm_bytesTb4b4b4)
T8b949e# Either store the encoded packet to return at the end of the
T8b949e# method or immediately call the callback with the encoded
T8b949e# packet.
Tff7b72def Td2a8ffstore_or_callbackTb4b4b4(Te6edf3encoded_packetTb4b4b4: Tffa657memoryviewTb4b4b4,
Te6edf3samplesTb4b4b4: Tffa657intTb4b4b4,
Te6edf3end_of_streamTb4b4b4: Tffa657bool Tff7b72= Tff7b72FalseTb4b4b4) Tff7b72-Tff7b72> Tff7b72NoneTb4b4b4:
Tff7b72if Te6edf3callback Tff7b72is Tff7b72NoneTb4b4b4:
T8b949e# Store the result
Te6edf3resultsTff7b72.Td2a8ffappendTb4b4b4(Tb4b4b4(
Te6edf3encoded_packetTb4b4b4,
Te6edf3samplesTb4b4b4,
Te6edf3end_of_stream
Tb4b4b4)Tb4b4b4)
Tff7b72elseTb4b4b4:
T8b949e# Call the callback
Te6edf3callbackTb4b4b4(
Te6edf3encoded_packetTb4b4b4,
Te6edf3samplesTb4b4b4,
Te6edf3end_of_stream
Tb4b4b4)

T8b949e# Fill the remainder of the buffer with silence and encode it.
T8b949e# The associated number of samples are only that of actual
T8b949e# data, not the added silence.
Tff7b72def Td2a8ffflush_bufferTb4b4b4(Tb4b4b4) Tff7b72-Tff7b72> Tff7b72NoneTb4b4b4:
T8b949e# Sanity checks to satisfy mypy
Tff7b72assert Tff7b72selfTff7b72.Td2a8ff_buffer_index Tff7b72is Tff7b72not Tff7b72None
Tff7b72assert Tff7b72selfTff7b72.Td2a8ff_channels Tff7b72is Tff7b72not Tff7b72None
Tff7b72assert Tff7b72selfTff7b72.Td2a8ff_buffer Tff7b72is Tff7b72not Tff7b72None
T8b949e# If the buffer is already empty, we have no work to do
Tff7b72if Tff7b72selfTff7b72.Td2a8ff_buffer_index Tff7b72== T79c0ff0Tb4b4b4:
Tff7b72return

T8b949e# Store the number of samples currently in the buffer
Te6edf3samples Tff7b72= Tb4b4b4(
Tff7b72selfTff7b72.Td2a8ff_buffer_index
Tff7b72/Tff7b72/ Tff7b72selfTff7b72.Td2a8ff_channels
Tff7b72/Tff7b72/ Te6edf3ctypesTff7b72.Td2a8ffsizeofTb4b4b4(Te6edf3opusTff7b72.Td2a8ffopus_int16Tb4b4b4)
Tb4b4b4)

T8b949e# Fill the buffer with silence
Te6edf3ctypesTff7b72.Td2a8ffmemsetTb4b4b4(
T8b949e# destination
Te6edf3ctypesTff7b72.Td2a8ffbyrefTb4b4b4(Tff7b72selfTff7b72.Td2a8ff_bufferTb4b4b4, Tff7b72selfTff7b72.Td2a8ff_buffer_indexTb4b4b4)Tb4b4b4,
T8b949e# value
T79c0ff0Tb4b4b4,
T8b949e# count
Tffa657lenTb4b4b4(Tff7b72selfTff7b72.Td2a8ff_bufferTb4b4b4) Tff7b72- Tff7b72selfTff7b72.Td2a8ff_buffer_index
Tb4b4b4)
T8b949e# Encode the PCM
T8b949e# As at 2020-11-05, mypy is unaware that ctype Arrays
T8b949e# support the buffer protocol.
Te6edf3encoded_packet Tff7b72= Tff7b72selfTff7b72.Td2a8ffencodeTb4b4b4(Tffa657memoryviewTb4b4b4(Tff7b72selfTff7b72.Td2a8ff_bufferTb4b4b4)Tb4b4b4) T8b949e# type: ignore

T8b949e# Either store the encoded packet or call the
T8b949e# callback
Te6edf3store_or_callbackTb4b4b4(Te6edf3encoded_packetTb4b4b4, Te6edf3samplesTb4b4b4, Tff7b72TrueTb4b4b4)

T8b949e# Copy the data remaining from the provided PCM into the
T8b949e# buffer. Flush if required.
Tff7b72def Td2a8ffcopy_insufficient_dataTb4b4b4(Tb4b4b4) Tff7b72-Tff7b72> Tff7b72NoneTb4b4b4:
T8b949e# Sanity checks to satisfy mypy
Tff7b72assert Tff7b72selfTff7b72.Td2a8ff_buffer Tff7b72is Tff7b72not Tff7b72None
T8b949e# Calculate remaining data
Te6edf3remaining_data Tff7b72= Tffa657lenTb4b4b4(Te6edf3pcm_bytesTb4b4b4) Tff7b72- Te6edf3pcm_index
T8b949e# Copy the data into the buffer.
Te6edf3ctypesTff7b72.Td2a8ffmemmoveTb4b4b4(
T8b949e# destination
Te6edf3ctypesTff7b72.Td2a8ffbyrefTb4b4b4(Tff7b72selfTff7b72.Td2a8ff_bufferTb4b4b4, Tff7b72selfTff7b72.Td2a8ff_buffer_indexTb4b4b4)Tb4b4b4,
T8b949e# source
Te6edf3ctypesTff7b72.Td2a8ffbyrefTb4b4b4(Te6edf3pcm_ctypesTb4b4b4, Te6edf3pcm_indexTb4b4b4)Tb4b4b4,
T8b949e# count
Te6edf3remaining_data
Tb4b4b4)

Tff7b72selfTff7b72.Td2a8ff_buffer_index Tff7b72+Tff7b72= Te6edf3remaining_data

T8b949e# If we've been asked to flush the buffer then do so
Tff7b72if Te6edf3flushTb4b4b4:
Te6edf3flush_bufferTb4b4b4(Tb4b4b4)
T8b949e# Loop through the provided PCM and the current buffer,
T8b949e# encoding as we have full packets.
Tff7b72while Tff7b72TrueTb4b4b4:
T8b949e# There are two possibilities at this point: either we
T8b949e# have previously unencoded data still in the buffer or we
T8b949e# do not
Tff7b72if Tff7b72selfTff7b72.Td2a8ff_buffer_index Tff7b72== T79c0ff0Tb4b4b4:
T8b949e# We do not have unencoded data

T8b949e# We are free to progress through the PCM that has
T8b949e# been provided encoding frames without copying any
T8b949e# bytes. Once there is insufficient data remaining
T8b949e# for a complete frame, that data should be copied
T8b949e# into the buffer and we have finished.
Tff7b72if Te6edf3pcm_len Tff7b72- Te6edf3pcm_index Tff7b72> Tff7b72selfTff7b72.Td2a8ff_frame_size_bytesTb4b4b4:
T8b949e# We have enough data remaining in the provided
T8b949e# PCM to encode more than an entire frame without
T8b949e# copying any data. Unfortunately, splicing a
T8b949e# ctypes array copies the array. To avoid the
T8b949e# copy we use memoryview see
T8b949e# https://mattgwwalker.wordpress.com/2020/12/12/python-ctypes-slicing/
Te6edf3frame_data Tff7b72= Tffa657memoryviewTb4b4b4(Te6edf3pcm_bytesTb4b4b4)Tb4b4b4[
Te6edf3pcm_indexTb4b4b4:Te6edf3pcm_indexTff7b72+Tff7b72selfTff7b72.Td2a8ff_frame_size_bytes
Tb4b4b4]

T8b949e# Update the PCM index
Te6edf3pcm_index Tff7b72+Tff7b72= Tff7b72selfTff7b72.Td2a8ff_frame_size_bytes

T8b949e# Store number of samples (per channel) of actual
T8b949e# data
Te6edf3samples Tff7b72= Tb4b4b4(
Tffa657lenTb4b4b4(Te6edf3frame_dataTb4b4b4)
Tff7b72/Tff7b72/ Tff7b72selfTff7b72.Td2a8ff_channels
Tff7b72/Tff7b72/ Te6edf3ctypesTff7b72.Td2a8ffsizeofTb4b4b4(Te6edf3opusTff7b72.Td2a8ffopus_int16Tb4b4b4)
Tb4b4b4)
T8b949e# Encode the PCM
Te6edf3encoded_packet Tff7b72= Tffa657superTb4b4b4(Tb4b4b4)Tff7b72.Td2a8ffencodeTb4b4b4(Te6edf3frame_dataTb4b4b4)

T8b949e# Either store the encoded packet or call the
T8b949e# callback
Te6edf3store_or_callbackTb4b4b4(Te6edf3encoded_packetTb4b4b4, Te6edf3samplesTb4b4b4)

Tff7b72elseTb4b4b4:
T8b949e# We do not have enough data to fill a frame while
T8b949e# still having data left over. Copy the data into
T8b949e# the buffer.
Te6edf3copy_insufficient_dataTb4b4b4(Tb4b4b4)
Tff7b72return Te6edf3results

Tff7b72elseTb4b4b4:
T8b949e# We have unencoded data.

T8b949e# Copy the provided PCM into the buffer (up until the
T8b949e# buffer is full). If we can fill it, then we can
T8b949e# encode the filled buffer and continue. If we can't
T8b949e# fill it then we've finished.
Te6edf3data_required Tff7b72= Tffa657lenTb4b4b4(Tff7b72selfTff7b72.Td2a8ff_bufferTb4b4b4) Tff7b72- Tff7b72selfTff7b72.Td2a8ff_buffer_index
Tff7b72if Te6edf3pcm_len Tff7b72> Te6edf3data_requiredTb4b4b4:
T8b949e# We have sufficient data to fill the buffer and
T8b949e# have data left over. Copy data into the buffer.
Tff7b72assert Te6edf3pcm_index Tff7b72== T79c0ff0
Te6edf3remaining Tff7b72= Tffa657lenTb4b4b4(Tff7b72selfTff7b72.Td2a8ff_bufferTb4b4b4) Tff7b72- Tff7b72selfTff7b72.Td2a8ff_buffer_index
Te6edf3ctypesTff7b72.Td2a8ffmemmoveTb4b4b4(
T8b949e# destination
Te6edf3ctypesTff7b72.Td2a8ffbyrefTb4b4b4(Tff7b72selfTff7b72.Td2a8ff_bufferTb4b4b4, Tff7b72selfTff7b72.Td2a8ff_buffer_indexTb4b4b4)Tb4b4b4,
T8b949e# source
Te6edf3pcm_ctypesTb4b4b4,
T8b949e# count
Te6edf3remaining
Tb4b4b4)
Te6edf3pcm_index Tff7b72+Tff7b72= Te6edf3remaining
Tff7b72selfTff7b72.Td2a8ff_buffer_index Tff7b72+Tff7b72= Te6edf3remaining
Tff7b72assert Tff7b72selfTff7b72.Td2a8ff_buffer_index Tff7b72== Tffa657lenTb4b4b4(Tff7b72selfTff7b72.Td2a8ff_bufferTb4b4b4)
T8b949e# Encode the PCM
Te6edf3encoded_packet Tff7b72= Tffa657superTb4b4b4(Tb4b4b4)Tff7b72.Td2a8ffencodeTb4b4b4(
T8b949e# Memoryviews of ctypes do work, even though
T8b949e# mypy complains.
Tffa657memoryviewTb4b4b4(Tff7b72selfTff7b72.Td2a8ff_bufferTb4b4b4) T8b949e# type: ignore
Tb4b4b4)

T8b949e# Store number of samples (per channel) of actual
T8b949e# data
Te6edf3samples Tff7b72= Tb4b4b4(
Tff7b72selfTff7b72.Td2a8ff_buffer_index
Tff7b72/Tff7b72/ Tff7b72selfTff7b72.Td2a8ff_channels
Tff7b72/Tff7b72/ Te6edf3ctypesTff7b72.Td2a8ffsizeofTb4b4b4(Te6edf3opusTff7b72.Td2a8ffopus_int16Tb4b4b4)
Tb4b4b4)

T8b949e# We've now processed the buffer
Tff7b72selfTff7b72.Td2a8ff_buffer_index Tff7b72= T79c0ff0
T8b949e# Either store the encoded packet or call the
T8b949e# callback
Te6edf3store_or_callbackTb4b4b4(Te6edf3encoded_packetTb4b4b4, Te6edf3samplesTb4b4b4)
Tff7b72elseTb4b4b4:
T8b949e# We have insufficient data to fill the buffer
T8b949e# while still having data left over. Copy the
T8b949e# data into the buffer.
Te6edf3copy_insufficient_dataTb4b4b4(Tb4b4b4)
Tff7b72return Te6edf3results


Tff7b72def Td2a8ff_calc_frame_sizeTb4b4b4(Tff7b72selfTb4b4b4)Tb4b4b4:
T8b949e"""Calculates the number of bytes in a frame.

If the frame size (in milliseconds) and the number of
samples per seconds have already been specified, then the
frame size in bytes is set. Otherwise, this method does
nothing.

The frame size is measured in bytes required to store the
sample.

"""
Tff7b72if Tb4b4b4(Tff7b72selfTff7b72.Td2a8ff_frame_size_ms Tff7b72is Tff7b72None
Tff7b72or Tff7b72selfTff7b72.Td2a8ff_samples_per_second Tff7b72is Tff7b72NoneTb4b4b4)Tb4b4b4:
Tff7b72return

Tff7b72selfTff7b72.Td2a8ff_frame_size_bytes Tff7b72= Tb4b4b4(
Tff7b72selfTff7b72.Td2a8ff_frame_size_ms
Tff7b72* Tff7b72selfTff7b72.Td2a8ff_samples_per_second
Tff7b72/Tff7b72/ T79c0ff1000
Tff7b72* Te6edf3ctypesTff7b72.Td2a8ffsizeofTb4b4b4(Te6edf3opusTff7b72.Td2a8ffopus_int16Tb4b4b4)
Tff7b72* Tff7b72selfTff7b72.Td2a8ff_channels
Tb4b4b4)

T8b949e# Allocate space for the buffer
Te6edf3Buffer Tff7b72= Te6edf3ctypesTff7b72.Td2a8ffc_ubyte Tff7b72* Tff7b72selfTff7b72.Td2a8ff_frame_size_bytes
Tff7b72selfTff7b72.Td2a8ff_buffer Tff7b72= Te6edf3BufferTb4b4b4(Tb4b4b4)


Tff7b72def Td2a8ff_get_next_frameTb4b4b4(Tff7b72selfTb4b4b4, Te6edf3add_silenceTff7b72=Tff7b72FalseTb4b4b4)Tb4b4b4:
T8b949e"""Gets the next Opus-encoded frame.

Returns a tuple where the first item is the Opus-encoded
frame and the second item is the number of encoded samples
(per channel).

Returns None if insufficient data is available.

"""
Te6edf3next_frame Tff7b72= Tffa657bytesTb4b4b4(Tb4b4b4)
Te6edf3samples Tff7b72= T79c0ff0

T8b949e# Ensure frame size has been specified
Tff7b72if Tff7b72selfTff7b72.Td2a8ff_frame_size_bytes Tff7b72is Tff7b72NoneTb4b4b4:
Tff7b72raise Te6edf3PyOggErrorTb4b4b4(
Ta5d6ff"Ta5d6ffDesired frame size hasnTa5d6ff'Ta5d6fft been set. Perhaps Ta5d6ff"Tff7b72+
Ta5d6ff"Ta5d6ffencode() was called before set_frame_size() Ta5d6ff"Tff7b72+
Ta5d6ff"Ta5d6ffand set_sampling_frequency()?Ta5d6ff"
Tb4b4b4)

T8b949e# Check if there's insufficient data in the buffer to fill
T8b949e# a frame.
Tff7b72if Tff7b72selfTff7b72.Td2a8ff_frame_size_bytes Tff7b72> Tff7b72selfTff7b72.Td2a8ff_buffer_sizeTb4b4b4:
Tff7b72if Tffa657lenTb4b4b4(Tff7b72selfTff7b72.Td2a8ff_bufferTb4b4b4) Tff7b72== T79c0ff0Tb4b4b4:
T8b949e# No data at all in buffer
Tff7b72return Tff7b72None
Tff7b72if Te6edf3add_silenceTb4b4b4:
T8b949e# Get all remaining data
Tff7b72while Tffa657lenTb4b4b4(Tff7b72selfTff7b72.Td2a8ff_bufferTb4b4b4) Tff7b72!= T79c0ff0Tb4b4b4:
Te6edf3next_frame Tff7b72+Tff7b72= Tff7b72selfTff7b72.Td2a8ff_bufferTff7b72.Td2a8ffpopleftTb4b4b4(Tb4b4b4)
Tff7b72selfTff7b72.Td2a8ff_buffer_size Tff7b72= T79c0ff0
T8b949e# Store number of samples (per channel) of actual
T8b949e# data
Te6edf3samples Tff7b72= Tb4b4b4(
Tffa657lenTb4b4b4(Te6edf3next_frameTb4b4b4)
Tff7b72/Tff7b72/ Tff7b72selfTff7b72.Td2a8ff_channels
Tff7b72/Tff7b72/ Te6edf3ctypesTff7b72.Td2a8ffsizeofTb4b4b4(Te6edf3opusTff7b72.Td2a8ffopus_int16Tb4b4b4)
Tb4b4b4)
T8b949e# Fill remainder of frame with silence
Te6edf3bytes_remaining Tff7b72= Tff7b72selfTff7b72.Td2a8ff_frame_size_bytes Tff7b72- Tffa657lenTb4b4b4(Te6edf3next_frameTb4b4b4)
Te6edf3next_frame Tff7b72+Tff7b72= Ta5d6ffbTa5d6ff'Tffea00\x00Ta5d6ff' Tff7b72* Te6edf3bytes_remaining
Tff7b72return Tb4b4b4(Te6edf3next_frameTb4b4b4, Te6edf3samplesTb4b4b4)
Tff7b72elseTb4b4b4:
T8b949e# Insufficient data to fill a frame and we're not
T8b949e# adding silence
Tff7b72return Tff7b72None

Te6edf3bytes_remaining Tff7b72= Tff7b72selfTff7b72.Td2a8ff_frame_size_bytes
Tff7b72while Te6edf3bytes_remaining Tff7b72> T79c0ff0Tb4b4b4:
Tff7b72if Tffa657lenTb4b4b4(Tff7b72selfTff7b72.Td2a8ff_bufferTb4b4b4[T79c0ff0Tb4b4b4]Tb4b4b4) Tff7b72<Tff7b72= Te6edf3bytes_remainingTb4b4b4:
T8b949e# Take the whole first item
Te6edf3buffer_ Tff7b72= Tff7b72selfTff7b72.Td2a8ff_bufferTff7b72.Td2a8ffpopleftTb4b4b4(Tb4b4b4)
Te6edf3next_frame Tff7b72+Tff7b72= Te6edf3buffer_
Te6edf3bytes_remaining Tff7b72-Tff7b72= Tffa657lenTb4b4b4(Te6edf3buffer_Tb4b4b4)
Tff7b72selfTff7b72.Td2a8ff_buffer_size Tff7b72-Tff7b72= Tffa657lenTb4b4b4(Te6edf3buffer_Tb4b4b4)
Tff7b72elseTb4b4b4:
T8b949e# Take only part of the buffer

T8b949e# TODO: This could be more efficiently
T8b949e# implemented. Rather than appending back the
T8b949e# remaining data, we could just update an index
T8b949e# saying where we were up to in regards to the
T8b949e# first entry of the buffer.
Te6edf3buffer_ Tff7b72= Tff7b72selfTff7b72.Td2a8ff_bufferTff7b72.Td2a8ffpopleftTb4b4b4(Tb4b4b4)
Te6edf3next_frame Tff7b72+Tff7b72= Te6edf3buffer_Tb4b4b4[Tb4b4b4:Te6edf3bytes_remainingTb4b4b4]
Tff7b72selfTff7b72.Td2a8ff_buffer_size Tff7b72-Tff7b72= Te6edf3bytes_remaining
T8b949e# And put the unused part back into the buffer
Tff7b72selfTff7b72.Td2a8ff_bufferTff7b72.Td2a8ffappendleftTb4b4b4(Te6edf3buffer_Tb4b4b4[Te6edf3bytes_remainingTb4b4b4:Tb4b4b4]Tb4b4b4)
Te6edf3bytes_remaining Tff7b72= T79c0ff0

T8b949e# Calculate number of samples (per channel)
Te6edf3samples Tff7b72= Tb4b4b4(
Tffa657lenTb4b4b4(Te6edf3next_frameTb4b4b4)
Tff7b72/Tff7b72/ Tff7b72selfTff7b72.Td2a8ff_channels
Tff7b72/Tff7b72/ Te6edf3ctypesTff7b72.Td2a8ffsizeofTb4b4b4(Te6edf3opusTff7b72.Td2a8ffopus_int16Tb4b4b4)
Tb4b4b4)

Tff7b72return Tb4b4b4(Te6edf3next_frameTb4b4b4, Te6edf3samplesTb4b4b4)


──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────